package com.kinghood.productcenter.service.base.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kinghood.infrastructure.api.authority.UserRemoteVo;
import com.kinghood.infrastructure.model.KingHoodException;
import com.kinghood.infrastructure.model.constant.CommonConstants;
import com.kinghood.infrastructure.model.dto.StatusReqDTO;
import com.kinghood.infrastructure.model.enums.StatusEnum;
import com.kinghood.infrastructure.util.UserUtil;
import com.kinghood.infrastructure.util.base.IdGenUtil;
import com.kinghood.productcenter.common.api.authority.UserRemote;
import com.kinghood.productcenter.common.model.dao.base.BaseCodeRuleDO;
import com.kinghood.productcenter.common.model.dao.base.BaseCodeRuleItemDO;
import com.kinghood.productcenter.common.model.dto.base.CodeRuleItemReqDTO;
import com.kinghood.productcenter.common.model.dto.base.CodeRuleReqDTO;
import com.kinghood.productcenter.common.model.dto.base.ListCodeRuleQueryDTO;
import com.kinghood.productcenter.common.model.enums.base.CodeRuleTypeEnum;
import com.kinghood.productcenter.common.model.vo.base.CodeRuleListVO;
import com.kinghood.productcenter.common.model.vo.base.CodeRuleValueVO;
import com.kinghood.productcenter.common.utils.UserNameUtil;
import com.kinghood.productcenter.dao.base.BaseCodeRuleMapper;
import com.kinghood.productcenter.service.base.IBaseCodeRuleItemService;
import com.kinghood.productcenter.service.base.IBaseCodeRuleService;
import com.kinghood.redis.util.RedisManager;
import com.kinghood.redis.util.RedissonManager;
import com.kinghood.sharding.model.KingHoodPageUtil;
import com.kinghood.sharding.model.KingPageResult;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

/**
 * <p>
 * 编码规则表 服务实现类
 * </p>
 *
 * @author KingHood
 * @since 2023-12-20
 */
@Service
public class BaseCodeRuleServiceImpl extends ServiceImpl<BaseCodeRuleMapper, BaseCodeRuleDO> implements IBaseCodeRuleService {
    @Resource
    private UserRemote userRemote;
    @Resource
    private IBaseCodeRuleItemService itemService;
    @Resource
    private RedisManager<String> redisManager;
    @Resource
    private RedissonManager redissonManager;

    /**
     * 列表
     *
     * @param param 参数
     * @return @return {@link Object }
     * @author jp
     * @date 2024/01/17 14:37
     */
    @Override
    public Object listCodeRule(ListCodeRuleQueryDTO param) {
        Page<CodeRuleListVO> result = this.baseMapper.listCodeRule(KingHoodPageUtil.getPage(param, param), param);

        Set<Long> userIdList = new HashSet<>();
        for (CodeRuleListVO record : result.getRecords()) {
            userIdList.add(record.getCreateUserId());
            userIdList.add(record.getUpdateUserId());
            record.setStatusTxt(StatusEnum.getNameByStatus(record.getStatus()));
        }
        userIdList.remove(null);
        if (!CollectionUtils.isEmpty(userIdList)) {
            Map<Long, UserRemoteVo> userMap = userRemote.getUserByIds(new ArrayList<Long>(userIdList));
            for (CodeRuleListVO record : result.getRecords()) {
                record.setCreateUserName(UserNameUtil.getName(record.getCreateUserId(), userMap));
                record.setUpdateUserName(UserNameUtil.getName(record.getUpdateUserId(), userMap));
            }
        }
        return KingPageResult.getResult(result);
    }

    /**
     * 新增更新
     *
     * @param param 参数
     * @return
     * @author jp
     * @date 2024/01/18 10:58
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addorUpdateCodeRule(CodeRuleReqDTO param) {
        checkCodeRuleOnly(param);
        checkSortRepeat(param);
        if (Objects.isNull(param.getId())) {
            addCodeRule(param);
        } else {
            updateCodeRule(param);
        }
    }

    /**
     * 检查是否唯一
     *
     * @param param 参数
     * @author jp
     * @date 2023/12/21
     */
    private void checkCodeRuleOnly(CodeRuleReqDTO param) {
        Long count;
        if (Objects.isNull(param.getId())) {
            count = this.lambdaQuery().eq(BaseCodeRuleDO::getRuleCode, param.getRuleCode())
                    .eq(BaseCodeRuleDO::getIsDelete, CommonConstants.IS_DELETE_N)
                    .count();
        } else {
            count = this.lambdaQuery().eq(BaseCodeRuleDO::getRuleCode, param.getRuleCode())
                    .ne(BaseCodeRuleDO::getId, param.getId())
                    .eq(BaseCodeRuleDO::getIsDelete, CommonConstants.IS_DELETE_N)
                    .count();
        }
        if (count > 0) {
            throw new KingHoodException(-1, "编码已存在");
        }
    }

    /**
     * 检查排序是否重复
     *
     * @param param 参数
     * @return
     * @author jp
     * @date 2024/01/18 17:54
     */
    private void checkSortRepeat(CodeRuleReqDTO param) {
        Set<Integer> sorts = new HashSet<>();
        for (CodeRuleItemReqDTO item : param.getItemList()) {
            if (sorts.contains(item.getSort())) {
                throw new KingHoodException(-1, "排序值不能重复");
            }
            sorts.add(item.getSort());
        }
    }

    /**
     * 新增编码规则
     *
     * @param param 参数
     * @return
     * @author jp
     * @date 2024/01/18 11:22
     */
    private void addCodeRule(CodeRuleReqDTO param) {
        LocalDateTime now = LocalDateTime.now();
        BaseCodeRuleDO codeRuleDO = new BaseCodeRuleDO();
        BeanUtils.copyProperties(param, codeRuleDO);
        codeRuleDO.setId(IdGenUtil.getId())
                .setStatus(CommonConstants.ENABLE)
                .setCreateTime(now)
                .setUpdateTime(now)
                .setCreateUserId(UserUtil.getUserId())
                .setUpdateUserId(UserUtil.getUserId());
        this.save(codeRuleDO);
        List<BaseCodeRuleItemDO> itemDOList = new ArrayList<>();
        for (CodeRuleItemReqDTO item : param.getItemList()) {
            BaseCodeRuleItemDO itemDO = new BaseCodeRuleItemDO();
            BeanUtils.copyProperties(item, itemDO);
            itemDO.setId(IdGenUtil.getId())
                    .setCodeRuleId(codeRuleDO.getId())
                    .setCreateTime(now)
                    .setUpdateTime(now)
                    .setCreateUserId(UserUtil.getUserId())
                    .setUpdateUserId(UserUtil.getUserId());
            itemDOList.add(itemDO);
        }
        itemService.saveBatch(itemDOList);
    }

    /**
     * 更新编码规则
     *
     * @param param 参数
     * @return
     * @author jp
     * @date 2024/01/18 11:53
     */
    private void updateCodeRule(CodeRuleReqDTO param) {
        BaseCodeRuleDO codeRuleDO = new BaseCodeRuleDO();
        LocalDateTime now = LocalDateTime.now();
        BaseCodeRuleDO oldCodeRule = this.getById(param.getId());
        BeanUtils.copyProperties(param, codeRuleDO);
        codeRuleDO.setRuleCode(oldCodeRule.getRuleCode())
                .setCreateTime(oldCodeRule.getCreateTime())
                .setCreateUserId(oldCodeRule.getCreateUserId())
                .setUpdateTime(now)
                .setUpdateUserId(UserUtil.getUserId());
        this.updateById(codeRuleDO);
        List<BaseCodeRuleItemDO> itemDOList = new ArrayList<>();
        for (CodeRuleItemReqDTO item : param.getItemList()) {
            BaseCodeRuleItemDO itemDO = new BaseCodeRuleItemDO();
            BeanUtils.copyProperties(item, itemDO);
            if (Objects.isNull(itemDO.getId())) {
                itemDO.setId(IdGenUtil.getId());
                itemDO.setCreateTime(now);
                itemDO.setCreateUserId(UserUtil.getUserId());
            }
            itemDO.setCodeRuleId(oldCodeRule.getId());
            itemDO.setUpdateUserId(itemDO.getCreateUserId());
            itemDO.setUpdateTime(now);
            itemDOList.add(itemDO);
        }
        // 删除不在当前集合中的标签值
        List<Long> updateIds = itemDOList.stream().map(BaseCodeRuleItemDO::getId)
                .collect(Collectors.toList());
        itemService.lambdaUpdate().set(BaseCodeRuleItemDO::getIsDelete, CommonConstants.IS_DELETE_Y)
                .set(BaseCodeRuleItemDO::getUpdateTime, now)
                .set(BaseCodeRuleItemDO::getUpdateUserId, UserUtil.getUserId())
                .eq(BaseCodeRuleItemDO::getCodeRuleId, param.getId())
                .notIn(!CollectionUtils.isEmpty(updateIds), BaseCodeRuleItemDO::getId, updateIds)
                .update();
        itemService.saveOrUpdateBatch(itemDOList);
    }

    /**
     * 删除
     *
     * @param id 身份证件
     * @return
     * @author jp
     * @date 2024/01/18 14:24
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delCodeRuleById(Long id) {
        BaseCodeRuleDO codeRuleDO = this.getById(id);
        /*if (Objects.nonNull(codeRuleDO) && !codeRuleDO.getStatus().equals(CommonConstants.DISABLE)) {
            throw new KingHoodException(-1 , "只有停用状态的才可操作删除");
        }*/
        itemService.lambdaUpdate().set(BaseCodeRuleItemDO::getIsDelete, CommonConstants.IS_DELETE_Y)
                .set(BaseCodeRuleItemDO::getUpdateTime, LocalDateTime.now())
                .set(BaseCodeRuleItemDO::getUpdateUserId, UserUtil.getUserId())
                .eq(BaseCodeRuleItemDO::getCodeRuleId, id)
                .eq(BaseCodeRuleItemDO::getIsDelete, CommonConstants.IS_DELETE_N)
                .update();
        this.lambdaUpdate().set(BaseCodeRuleDO::getIsDelete, CommonConstants.IS_DELETE_Y)
                .set(BaseCodeRuleDO::getUpdateTime, LocalDateTime.now())
                .set(BaseCodeRuleDO::getUpdateUserId, UserUtil.getUserId())
                .eq(BaseCodeRuleDO::getId, id)
                .eq(BaseCodeRuleDO::getIsDelete, CommonConstants.IS_DELETE_N)
                .update();
    }

    /**
     * 根据ID查询详情
     *
     * @param id 身份证件
     * @return @return {@link Object }
     * @author jp
     * @date 2024/01/18 14:34
     */
    @Override
    public CodeRuleReqDTO detailCodeRule(Long id) {
        CodeRuleReqDTO result = new CodeRuleReqDTO();
        BaseCodeRuleDO codeRuleDO = this.lambdaQuery().eq(BaseCodeRuleDO::getIsDelete, CommonConstants.IS_DELETE_N)
                .eq(BaseCodeRuleDO::getId, id)
                .one();
        if (Objects.nonNull(codeRuleDO)) {
            BeanUtils.copyProperties(codeRuleDO, result);
            List<CodeRuleItemReqDTO> values = itemService.lambdaQuery()
                    .eq(BaseCodeRuleItemDO::getIsDelete, CommonConstants.IS_DELETE_N)
                    .eq(BaseCodeRuleItemDO::getCodeRuleId, id)
                    .orderByDesc(BaseCodeRuleItemDO::getSort, BaseCodeRuleItemDO::getCreateTime)
                    .list()
                    .stream().map(i -> {
                        CodeRuleItemReqDTO item = new CodeRuleItemReqDTO();
                        BeanUtils.copyProperties(i, item);
                        return item;
                    }).collect(Collectors.toList());
            result.setItemList(values);
            return result;
        }
        return null;
    }

    /**
     * 更新状态
     *
     * @param param 参数
     * @return
     * @author jp
     * @date 2024/01/18 14:46
     */
    @Override
    public void updateCodeRuleStatus(StatusReqDTO param) {
        this.lambdaUpdate().set(BaseCodeRuleDO::getStatus, param.getStatus())
                .set(BaseCodeRuleDO::getUpdateTime, LocalDateTime.now())
                .set(BaseCodeRuleDO::getUpdateUserId, UserUtil.getUserId())
                .eq(BaseCodeRuleDO::getId, param.getId())
                .eq(BaseCodeRuleDO::getIsDelete, CommonConstants.IS_DELETE_N)
                .update();
    }

    /**
     * 获取编码
     *
     * @param code 密码
     * @return @return {@link Object }
     * @author jp
     * @date 2024/01/18 17:34
     */
    @Override
    public Object getCodeByRule(String code) {
        BaseCodeRuleDO codeRuleDO = this.lambdaQuery()
                .eq(BaseCodeRuleDO::getRuleCode, code)
                .eq(BaseCodeRuleDO::getIsDelete, CommonConstants.IS_DELETE_N)
                .one();
        if (Objects.isNull(codeRuleDO)) {
            throw new KingHoodException(-1, "编码规则不存在");
        }
        if (codeRuleDO.getStatus() == CommonConstants.DISABLE) {
            return null;
        }
        List<BaseCodeRuleItemDO> ruleItemDOS = itemService.lambdaQuery()
                .eq(BaseCodeRuleItemDO::getCodeRuleId, codeRuleDO.getId())
                .eq(BaseCodeRuleItemDO::getIsDelete, CommonConstants.IS_DELETE_N)
                .orderByDesc(BaseCodeRuleItemDO::getSort)
                .list();
        List<CodeRuleValueVO> result = new ArrayList<>();
        for (BaseCodeRuleItemDO ruleItemDO : ruleItemDOS) {
            CodeRuleValueVO valueVO = new CodeRuleValueVO();
            valueVO.setRuleType(ruleItemDO.getRuleType());
            valueVO.setValue(getValue(ruleItemDO));
            result.add(valueVO);
        }
        return result;
    }

    /**
     * 获取规则的值
     *
     * @param ruleItemDO 规则项do
     * @return @return {@link String }
     * @author jp
     * @date 2024/01/22 11:29
     */
    private String getValue(BaseCodeRuleItemDO ruleItemDO) {
        // 流水号
        if (ruleItemDO.getRuleType().compareTo(CodeRuleTypeEnum.SERIAL_NUMBER.getType()) == 0) {
            if (ruleItemDO.getNumberType() == 1) {
                String key = "RULE_SELF_INCREASING:" + ruleItemDO.getCodeRuleId() + ":" + ruleItemDO.getId();
                Object result = null;
                try {
                    result = redissonManager.tryLockhasResult(key + "lock", 5, () -> {
                        String value = redisManager.getValueOps().get(key);
                        if (StringUtils.isEmpty(value)) {
                            if (StringUtils.isEmpty(ruleItemDO.getContent())) {
                                int n = 1;
                                if (!Objects.isNull(ruleItemDO.getNumberSize())) {
                                    n = ruleItemDO.getNumberSize(); // 设置需要的位数
                                }
                                double minValue = Math.pow(10, n - 1); // 计算10^（n-1），即当前位数的最小值
                                long min_val = Math.round(minValue);
                                value = String.valueOf(min_val);
                                redisManager.getValueOps().set(key, value);
                            } else {
                                value = ruleItemDO.getContent();
                                redisManager.getValueOps().set(key, value);
                            }
                        } else {
                            value = String.valueOf(Long.parseLong(value) + 1);
                            redisManager.getValueOps().set(key, value);
                        }
                        return value;
                    });
                } catch (Exception e) {
                    log.error("获取自增值异常：", e);
                    throw new RuntimeException(e);
                }
                return result.toString();

            } else if (ruleItemDO.getNumberType() == 2) {
                int n = 1;
                if (!Objects.isNull(ruleItemDO.getNumberSize())) {
                    n = ruleItemDO.getNumberSize(); // 设置需要的位数
                }
                double minValue = Math.pow(10, n - 1); // 计算10^（n-1），即当前位数的最小值
                long min_val = Math.round(minValue);
                long max_val = 999999999999999L;
                ThreadLocalRandom tlr = ThreadLocalRandom.current();
                long randomNum = tlr.nextLong(min_val, max_val + 1);
                return String.valueOf(randomNum);
            }
            // 常量
        } else if (ruleItemDO.getRuleType().compareTo(CodeRuleTypeEnum.CONSTANT.getType()) == 0) {
            return ruleItemDO.getContent();
            // 时间
        } else if (ruleItemDO.getRuleType().compareTo(CodeRuleTypeEnum.DATE.getType()) == 0) {
            String format;
            if ("yy".equals(ruleItemDO.getContent())) {
                format = "yy";
            } else if ("yymm".equals(ruleItemDO.getContent())) {
                format = "yyMM";
            } else if ("yymmdd".equals(ruleItemDO.getContent())) {
                format = "yyMMdd";
            } else {
                throw new KingHoodException(-1, "规则时间数据异常");
            }
            DateTimeFormatter df = DateTimeFormatter.ofPattern(format);
            return df.format(LocalDate.now());
            // 变量
        } else if (ruleItemDO.getRuleType().compareTo(CodeRuleTypeEnum.VARIABLE.getType()) == 0) {
            return ruleItemDO.getContent();
        }
        throw new KingHoodException(-1, "规则类型数据异常");
    }
}
